{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom Components"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The majority of the time the default [ft components](/explains/explaining_xt_components.html) are all you need (for example `Div`, `P`, `H1`, etc.).\n",
    "\n",
    ":::{.callout-tip}\n",
    "\n",
    "##### Pre-requisite Knowledge\n",
    "If you don't know what an ft component is, you should read [the explaining ft components explainer first](/explains/explaining_xt_components.html).\n",
    ":::\n",
    "\n",
    "\n",
    "However, there are many situations where you need a custom ft component that creates a unique HTML tag (for example `<zero-md></zero-md>`).  There are many options in FastHTML to do this, and this section will walk through them.  Generally you want to use the highest level option that fits your needs.\n",
    "\n",
    ":::{.callout-tip}\n",
    "##### Real-world example\n",
    "[This external tutorial](https://isaac-flath.github.io/website/posts/boots/FasthtmlTutorial.html) walks through a practical situation where you may want to create a custom HTML tag using a custom ft component.  Seeing a real-world example is a good way to understand why the contents of this guide is useful.\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automatic Creation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first (and in most cases the best) approach is to let FastHTML generate the component function for you.  As you can see in our `assert` this creates a function that creates the HTML just as we wanted.  This works even though there is not a `Some_never_before_used_tag` function in the `fasthtml.components` source code (you can verify this yourself by looking at the source code).  \n",
    "\n",
    ":::{.callout-tip}\n",
    "Typically these tags are needed because a CSS or Javascript library created a new XML tag that isn't default HTML.  For example the `zero-md` javascript library looks for a `<zero-md></zero-md>` tag to know what to run its javascript code on.  Most CSS libraries work by creating styling based on the `class` attribute, but they can also apply styling to an arbitrary HTML tag that they made up.\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```html\n",
       "<some-never-before-used-tag></some-never-before-used-tag>\n",
       "```"
      ],
      "text/plain": [
       "some-never-before-used-tag((),{})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from fasthtml.components import Some_never_before_used_tag\n",
    "\n",
    "Some_never_before_used_tag()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{.callout-tip}\n",
    "In a module `__getattr__` is called to get an attribute.  In `fasthtml.components`, the `__getattr__` is set to create components automatically for you.\n",
    "\n",
    "Dunder methods and functions are special functions that have double underscores at the beginning and end of their name.  They are called at specific times in python so you can use them to cause customized behavior that makes sense for your specific use case.  They can appear magical if you don't know how python works, but they are extremely commonly used to modify python's default behavior (`__init__`  is probably the most common one).\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Manual Creation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want a component that creates `<path></path>` that doesn't conflict names with `pathlib.Path` you can do that.  FastHTML automatically creates new components with a 1:1 mapping and a consistent name, which is almost always what you want.  But in some cases you may want to customize that and you can use the `ft_hx` function to do that differently than the default."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```html\n",
       "<path></path>\n",
       "```"
      ],
      "text/plain": [
       "path((),{})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from fasthtml.common import ft_hx\n",
    "\n",
    "def ft_path(*c, target_id=None, **kwargs): \n",
    "    return ft_hx('path', *c, target_id=target_id, **kwargs)\n",
    "\n",
    "ft_path()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Special Cases"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can add any behavior in that function that we need to, so let's go through some progressively complex examples that you may need in some of your projects."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Underscores in tags"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we understand how FastHTML generates components, we can create our own in all kinds of ways.  For example, maybe we need a weird HTML tag that uses underscores.  FastHTML replaces `_` with `-` in tags because underscores in tags are highly unusual and rarely what you want, though it does come up rarely."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```html\n",
       "<tag_with_underscores></tag_with_underscores>\n",
       "```"
      ],
      "text/plain": [
       "tag_with_underscores((),{})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def tag_with_underscores(*c, target_id=None, **kwargs): \n",
    "    return ft_hx('tag_with_underscores', *c, target_id=target_id, **kwargs)\n",
    "\n",
    "tag_with_underscores()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Symbols (ie @) in tags"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes you may need to use a tag that uses characters that are not allowed in function names in python (again, very unusual)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```html\n",
       "<tag-with-@symbol></tag-with-@symbol>\n",
       "```"
      ],
      "text/plain": [
       "tag-with-@symbol((),{})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def tag_with_AtSymbol(*c, target_id=None, **kwargs): \n",
    "    return ft_hx('tag-with-@symbol', *c, target_id=target_id, **kwargs)\n",
    "\n",
    "tag_with_AtSymbol()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Symbols (ie @) in tag attributes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It also may be that an argument in an HTML tag uses characters that can't be used in python arguments.  To handle these you can define those args using a dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fasthtml.common import Div"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```html\n",
       "<div normal-arg=\"normal stuff\" notnormal:arg:with_varing@symbols!=\"123\"></div>\n",
       "\n",
       "```"
      ],
      "text/plain": [
       "div((),{'normal-arg': 'normal stuff', 'notnormal:arg:with_varing@symbols!': '123'})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Div(normal_arg='normal stuff',**{'notNormal:arg:with_varing@symbols!':'123'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## In Desperation: NotStr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An uncommon method is to use the `NotStr` class writing the HTML tag as a string.  It works as a one-off for a quickfix but quickly becomes harder to work with as complexity grows.  However we can see that you can with some effort generate a similar result with `to_xml` using `NotStr` as the out-of-the-box components.\n",
    "\n",
    "Try everything else before using `NotStr` and if you must use it we recommend returning later and fixing it to use one of the above techniques."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fasthtml.common import NotStr,to_xml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'<div>I look like a string but render with to_xml</div>'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "div_NotStr = NotStr('<div>I look like a string but render with to_xml</div>') \n",
    "to_xml(div_NotStr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
